package com.weifly.weistock.record.option.impl;

import com.weifly.weistock.core.common.StockException;
import com.weifly.weistock.record.base.domain.RecordPair;
import com.weifly.weistock.record.option.OptionConst;
import com.weifly.weistock.record.option.OptionStoreService;
import com.weifly.weistock.record.option.OptionUtils;
import com.weifly.weistock.record.option.domain.OptionRecordDto;
import com.weifly.weistock.record.option.domain.OptionYearDto;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 期权交易记录存储服务
 *
 * @author weifly
 * @since 2019/11/18
 */
public class OptionStoreServiceImpl implements OptionStoreService {

    private Logger log = LoggerFactory.getLogger(OptionStoreServiceImpl.class);

    private static final String ELE_ROOT = "root";
    private static final String ELE_RECORD = "record";
    private static final String ATTR_YEAR = "year";
    private static final String ATTR_DATE = "date";
    private static final String ATTR_TIME = "time";
    private static final String ATTR_CONTRACT_NAME = "contractName";
    private static final String ATTR_CONTRACT_CODE = "contractCode";
    private static final String ATTR_STOCK_NAME = "stockName";
    private static final String ATTR_STOCK_CODE = "stockCode";
    private static final String ATTR_BUSINESS_NAME = "businessName";
    private static final String ATTR_OPERATION_TYPE = "operationType";
    private static final String ATTR_HOLD_POSITION = "holdPosition";
    private static final String ATTR_TRADE_PRICE = "tradePrice";
    private static final String ATTR_TRADE_NUMBER = "tradeNumber";
    private static final String ATTR_TRADE_AMOUNT = "tradeAmount";
    private static final String ATTR_FEE_SERVICE = "feeService";
    private static final String ATTR_FEE_STAMP = "feeStamp";
    private static final String ATTR_FEE_TRANSFER = "feeTransfer";
    private static final String ATTR_FEE_EXTRA = "feeExtra";
    private static final String ATTR_FEE_CLEAR = "feeClear";
    private static final String ATTR_CLEAR_AMOUNT = "clearAmount";
    private static final String ATTR_AFTER_AMOUNT = "afterAmount";
    private static final String ATTR_AFTER_NUMBER = "afterNumber";
    private static final String ATTR_ENTRUST_CODE = "entrustCode";
    private static final String ATTR_PAIR = "pair";
    private static final String ATTR_DIFF_AMOUNT = "diffAmount";

    private String storePath; // 交易存储路径

    @Override
    public void setConfigPath(String configPath) {
        this.storePath = configPath;
    }

    @Override
    public List<OptionYearDto> loadYearList() {
        List<OptionYearDto> yearList = new ArrayList<>();

        File storeFolder = new File(this.storePath);
        if(!storeFolder.exists()){
            log.info("期权记录目录不存在：" + storeFolder.getAbsolutePath());
            return yearList;
        }

        for(File xmlFile : storeFolder.listFiles()){
            if(xmlFile.getName().endsWith(".xml")){
                log.info("加载期权记录：" + xmlFile.getAbsolutePath());
                OptionYearDto yearDto = this.loadYearXml(xmlFile);
                yearList.add(yearDto);
            }
        }
        return yearList;
    }

    private OptionYearDto loadYearXml(File xmlFile){
        OptionYearDto yearDto = new OptionYearDto();
        try{
            SAXReader reader = new SAXReader();
            Document document = reader.read(xmlFile);
            Element rootEle = document.getRootElement();

            String year = rootEle.attributeValue(ATTR_YEAR);
            yearDto.setYear(year);

            List<Element> recordEleList = rootEle.elements(ELE_RECORD);
            if(recordEleList!=null){
                for(Element recordEle : recordEleList){
                    OptionRecordDto recordDto = new OptionRecordDto();
                    // 读属性
                    recordDto.setDate(recordEle.attributeValue(ATTR_DATE));
                    recordDto.setTime(recordEle.attributeValue(ATTR_TIME));
                    recordDto.setContractName(recordEle.attributeValue(ATTR_CONTRACT_NAME));
                    recordDto.setContractCode(recordEle.attributeValue(ATTR_CONTRACT_CODE));
                    recordDto.setStockName(recordEle.attributeValue(ATTR_STOCK_NAME));
                    recordDto.setStockCode(recordEle.attributeValue(ATTR_STOCK_CODE));
                    // 业务名称
                    String businessNameValue = recordEle.attributeValue(ATTR_BUSINESS_NAME);
                    OptionConst.BusinessName businessNameEnum = OptionConst.BusinessName.getEnumByName(businessNameValue);
                    if(businessNameEnum==null){
                        throw new StockException("不支持的businessName: " + businessNameValue);
                    }
                    recordDto.setBusinessName(businessNameEnum);
                    // 买卖标志
                    String operationTypeValue = recordEle.attributeValue(ATTR_OPERATION_TYPE);
                    OptionConst.OperationType operationTypeEnum = OptionConst.OperationType.getEnumByName(operationTypeValue);
                    if(operationTypeEnum==null){
                        throw new StockException("不支持的operationType: " + operationTypeValue);
                    }
                    recordDto.setOperationType(operationTypeEnum);
                    // 持仓方向
                    String holdPositionValue = recordEle.attributeValue(ATTR_HOLD_POSITION);
                    OptionConst.HoldPosition holdPositionEnum = OptionConst.HoldPosition.getEnumByName(holdPositionValue);
                    if(holdPositionEnum==null){
                        throw new StockException("不支持的holdPosition: " + holdPositionValue);
                    }
                    recordDto.setHoldPosition(holdPositionEnum);
                    // 成交价格
                    recordDto.setTradePrice(Double.valueOf(recordEle.attributeValue(ATTR_TRADE_PRICE)));
                    // 成交数量
                    recordDto.setTradeNumber(Integer.valueOf(recordEle.attributeValue(ATTR_TRADE_NUMBER)));
                    // 成交金额
                    recordDto.setTradeAmount(Double.valueOf(recordEle.attributeValue(ATTR_TRADE_AMOUNT)));
                    // 手续费
                    recordDto.setFeeService(Double.valueOf(recordEle.attributeValue(ATTR_FEE_SERVICE)));
                    // 印花税
                    recordDto.setFeeStamp(Double.valueOf(recordEle.attributeValue(ATTR_FEE_STAMP)));
                    // 过户费
                    recordDto.setFeeTransfer(Double.valueOf(recordEle.attributeValue(ATTR_FEE_TRANSFER)));
                    // 附加费
                    recordDto.setFeeExtra(Double.valueOf(recordEle.attributeValue(ATTR_FEE_EXTRA)));
                    // 交易所清算费
                    recordDto.setFeeClear(Double.valueOf(recordEle.attributeValue(ATTR_FEE_CLEAR)));
                    // 清算金额
                    recordDto.setClearAmount(Double.valueOf(recordEle.attributeValue(ATTR_CLEAR_AMOUNT)));
                    // 资金本次余额
                    recordDto.setAfterAmount(Double.valueOf(recordEle.attributeValue(ATTR_AFTER_AMOUNT)));
                    // 剩余数量
                    recordDto.setAfterNumber(Integer.valueOf(recordEle.attributeValue(ATTR_AFTER_NUMBER)));
                    // 委托编号
                    recordDto.setEntrustCode(recordEle.attributeValue(ATTR_ENTRUST_CODE));
                    // 配对信息
                    List<RecordPair> pairList = this.parsePairAttr(recordDto, recordEle.attributeValue(ATTR_PAIR));
                    if(pairList!=null){
                        recordDto.setPairList(pairList);
                    }
                    // 配对结算金额
                    String diffAmountValue = recordEle.attributeValue(ATTR_DIFF_AMOUNT);
                    if(StringUtils.isNotBlank(diffAmountValue)){
                        recordDto.setDiffAmount(Double.valueOf(diffAmountValue));
                    }
                    OptionUtils.addRecordToYear(yearDto, recordDto, null);
                }
            }
            return yearDto;
        }catch(DocumentException e){
            log.error("", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveOptionYear(OptionYearDto yearDto) {
        File storeFolder = new File(this.storePath);
        if(!storeFolder.exists()){
            storeFolder.mkdirs();
        }

        DocumentFactory factory = DocumentFactory.getInstance();

        Document document = factory.createDocument();
        document.setXMLEncoding("UTF8");

        Element rootEle = factory.createElement(ELE_ROOT);
        rootEle.addAttribute(ATTR_YEAR, yearDto.getYear());
        document.setRootElement(rootEle);

        for(OptionRecordDto recordDto : yearDto.getRecordList()){
            Element recordEle = factory.createElement(ELE_RECORD);
            recordEle.addAttribute(ATTR_DATE, recordDto.getDate());
            recordEle.addAttribute(ATTR_TIME, recordDto.getTime());
            recordEle.addAttribute(ATTR_CONTRACT_NAME, recordDto.getContractName());
            recordEle.addAttribute(ATTR_CONTRACT_CODE, recordDto.getContractCode());
            recordEle.addAttribute(ATTR_STOCK_NAME, recordDto.getStockName());
            recordEle.addAttribute(ATTR_STOCK_CODE, recordDto.getStockCode());
            recordEle.addAttribute(ATTR_BUSINESS_NAME, recordDto.getBusinessName().getName());
            recordEle.addAttribute(ATTR_OPERATION_TYPE, recordDto.getOperationType().getName());
            recordEle.addAttribute(ATTR_HOLD_POSITION, recordDto.getHoldPosition().getName());
            recordEle.addAttribute(ATTR_TRADE_PRICE, recordDto.getTradePrice().toString());
            recordEle.addAttribute(ATTR_TRADE_NUMBER, recordDto.getTradeNumber().toString());
            recordEle.addAttribute(ATTR_TRADE_AMOUNT, recordDto.getTradeAmount().toString());
            recordEle.addAttribute(ATTR_FEE_SERVICE, recordDto.getFeeService().toString());
            recordEle.addAttribute(ATTR_FEE_STAMP, recordDto.getFeeStamp().toString());
            recordEle.addAttribute(ATTR_FEE_TRANSFER, recordDto.getFeeTransfer().toString());
            recordEle.addAttribute(ATTR_FEE_EXTRA, recordDto.getFeeExtra().toString());
            recordEle.addAttribute(ATTR_FEE_CLEAR, recordDto.getFeeClear().toString());
            recordEle.addAttribute(ATTR_CLEAR_AMOUNT, recordDto.getClearAmount().toString());
            recordEle.addAttribute(ATTR_AFTER_AMOUNT, recordDto.getAfterAmount().toString());
            recordEle.addAttribute(ATTR_AFTER_NUMBER, recordDto.getAfterNumber().toString());
            recordEle.addAttribute(ATTR_ENTRUST_CODE, recordDto.getEntrustCode());
            String pairAttr = OptionUtils.makePairAttr(recordDto);
            if(pairAttr!=null){
                recordEle.addAttribute(ATTR_PAIR, pairAttr);
            }
            if(recordDto.getDiffAmount()!=null){
                recordEle.addAttribute(ATTR_DIFF_AMOUNT, recordDto.getDiffAmount().toString());
            }
            rootEle.add(recordEle);
        }

        FileOutputStream output = null;
        try{
            File xmlFile = new File(storeFolder, "option_" + yearDto.getYear() + ".xml");
            log.info("保存期权交易记录, file={}", xmlFile.getAbsolutePath());
            output = new FileOutputStream(xmlFile);
            OutputFormat format = new OutputFormat("  ", true, "UTF8");
            XMLWriter writer = new XMLWriter(output, format);
            writer.write(document);
            writer.close();
        }catch(IOException e){
            throw new RuntimeException(e);
        }finally {
            IOUtils.closeQuietly(output);
        }
    }

    private List<RecordPair> parsePairAttr(OptionRecordDto record, String pairAttr){
        if(StringUtils.isBlank(pairAttr)){
            return null;
        }
        List<RecordPair> pairList = new ArrayList<>();
        String[] parts = pairAttr.split(",");
        for(String p : parts){
            RecordPair pair = new RecordPair();
            if(p.indexOf("(")==-1){
                pair.setIndex(Integer.parseInt(p));
                pair.setNumber(record.getTradeNumber());
            }else{
                int startIdx = p.indexOf("(");
                int endIdx = p.indexOf(")");
                pair.setIndex(Integer.parseInt(p.substring(0, startIdx)));
                pair.setNumber(Integer.parseInt(p.substring(startIdx+1, endIdx)));
            }
            pairList.add(pair);
        }
        return pairList;
    }
}
